summaryrefslogtreecommitdiff
path: root/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-29 08:01:53 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-29 08:01:53 +0000
commitc7d37ec3e60c9197abc79738316ddae7c5bf8817 (patch)
tree9b045c7b7302d55f43d76565aa4fd5c6dd3a097b /app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx
parent82a2ce067c9b690cdf7775dfb0be94583f51ca29 (diff)
(대표님) 그룹라우터로 앱라우터 경로 정리
Diffstat (limited to 'app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx')
-rw-r--r--app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx380
1 files changed, 0 insertions, 380 deletions
diff --git a/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx b/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx
deleted file mode 100644
index f8a75641..00000000
--- a/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx
+++ /dev/null
@@ -1,380 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { Loader2, Users, Building2, AlertCircle } from "lucide-react";
-import { Button } from "@/components/ui/button";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from "@/components/ui/dialog";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-import { Textarea } from "@/components/ui/textarea";
-import { Badge } from "@/components/ui/badge";
-import { Label } from "@/components/ui/label";
-import { Separator } from "@/components/ui/separator";
-import {
- Table,
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
-} from "@/components/ui/table";
-import {
- DepartmentNode
-} from "@/lib/users/knox-service";
-import {
- getDepartmentDomainAssignmentsByDepartments
-} from "@/lib/users/department-domain/service";
-import { DOMAIN_OPTIONS, getDomainLabel } from "./domain-constants";
-
-interface ExistingAssignment {
- id: number;
- companyCode: string;
- departmentCode: string;
- departmentName: string;
- assignedDomain: string;
- description?: string | null;
- createdAt: Date;
- updatedAt: Date;
-}
-
-interface DepartmentDomainAssignmentDialogProps {
- open: boolean;
- onOpenChange: (open: boolean) => void;
- selectedDepartments: string[];
- departments: DepartmentNode[];
- companyInfo: { code: string; name: string };
- onAssign: (assignments: {
- departmentCodes: string[];
- domain: string;
- description?: string;
- }) => Promise<void>;
- isLoading?: boolean;
-}
-
-export function DepartmentDomainAssignmentDialog({
- open,
- onOpenChange,
- selectedDepartments,
- departments,
- companyInfo,
- onAssign,
- isLoading = false,
-}: DepartmentDomainAssignmentDialogProps) {
- const [selectedDomain, setSelectedDomain] = React.useState<string>("");
- const [description, setDescription] = React.useState<string>("");
- const [isSubmitting, setIsSubmitting] = React.useState(false);
- const [existingAssignments, setExistingAssignments] = React.useState<ExistingAssignment[]>([]);
- const [isLoadingAssignments, setIsLoadingAssignments] = React.useState(false);
-
- // 선택된 부서들의 정보 가져오기
- const getSelectedDepartmentInfo = React.useCallback(() => {
- const findDepartment = (nodes: DepartmentNode[], code: string): DepartmentNode | null => {
- for (const node of nodes) {
- if (node.departmentCode === code) {
- return node;
- }
- const found = findDepartment(node.children, code);
- if (found) return found;
- }
- return null;
- };
-
- return selectedDepartments
- .map(code => findDepartment(departments, code))
- .filter(Boolean) as DepartmentNode[];
- }, [departments, selectedDepartments]);
-
- // 회사별로 그룹화
- const selectedDepartmentsByCompany = React.useMemo(() => {
- const deptInfo = getSelectedDepartmentInfo();
- const grouped = new Map<string, DepartmentNode[]>();
-
- deptInfo.forEach(dept => {
- if (!grouped.has(dept.companyCode)) {
- grouped.set(dept.companyCode, []);
- }
- grouped.get(dept.companyCode)!.push(dept);
- });
-
- return grouped;
- }, [getSelectedDepartmentInfo]);
-
- // 기존 할당 정보 조회
- React.useEffect(() => {
- if (open && selectedDepartments.length > 0) {
- const loadExistingAssignments = async () => {
- setIsLoadingAssignments(true);
- try {
- const assignments = await getDepartmentDomainAssignmentsByDepartments(selectedDepartments);
- setExistingAssignments(assignments as ExistingAssignment[]);
- } catch (error) {
- console.error("기존 할당 정보 조회 실패:", error);
- setExistingAssignments([]);
- } finally {
- setIsLoadingAssignments(false);
- }
- };
-
- loadExistingAssignments();
- } else {
- setExistingAssignments([]);
- }
- }, [open, selectedDepartments]);
-
- // 폼 초기화
- React.useEffect(() => {
- if (open) {
- setSelectedDomain("");
- setDescription("");
- setIsSubmitting(false);
- }
- }, [open]);
-
- // 할당 처리
- const handleAssign = async () => {
- if (!selectedDomain || selectedDepartments.length === 0) {
- return;
- }
-
- setIsSubmitting(true);
-
- try {
- await onAssign({
- departmentCodes: selectedDepartments,
- domain: selectedDomain,
- description: description.trim() || undefined,
- });
-
- // 성공 시 다이얼로그 닫기
- onOpenChange(false);
- } catch (error) {
- console.error("도메인 할당 실패:", error);
- } finally {
- setIsSubmitting(false);
- }
- };
-
- const canSubmit = selectedDomain && selectedDepartments.length > 0 && !isSubmitting && !isLoading;
- const selectedDomainInfo = DOMAIN_OPTIONS.find(opt => opt.value === selectedDomain);
- const hasConflicts = existingAssignments.some(a => a.assignedDomain !== selectedDomain && selectedDomain);
-
- return (
- <Dialog open={open} onOpenChange={onOpenChange}>
- <DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
- <DialogHeader>
- <DialogTitle className="flex items-center gap-2">
- <Building2 className="h-5 w-5" />
- 부서별 도메인 할당
- </DialogTitle>
- <DialogDescription>
- 선택된 {selectedDepartments.length}개 부서에 도메인을 할당합니다.
- 상위 부서를 선택한 경우 하위 부서들도 자동으로 포함됩니다.
- </DialogDescription>
- </DialogHeader>
-
- <div className="flex-1 overflow-y-auto px-1">
- <div className="space-y-6 pr-3">
- {/* 선택된 부서들 표시 */}
- <div className="space-y-3">
- <Label className="text-sm font-medium flex items-center gap-2">
- <Users className="h-4 w-4" />
- 선택된 부서 ({selectedDepartments.length}개)
- </Label>
-
- <div className="border rounded-md p-3 max-h-40 overflow-y-auto">
- {Array.from(selectedDepartmentsByCompany.entries()).map(([companyCode, depts]) => (
- <div key={companyCode} className="mb-3 last:mb-0">
- <div className="text-sm font-medium text-muted-foreground mb-2">
- {companyCode} - {companyInfo.name}
- </div>
- <div className="flex flex-wrap gap-2">
- {depts.map((dept) => (
- <Badge
- key={dept.departmentCode}
- variant="outline"
- className="text-xs"
- >
- {dept.departmentName || dept.departmentCode}
- </Badge>
- ))}
- </div>
- </div>
- ))}
- </div>
- </div>
-
- {/* 기존 할당 현황 */}
- {(existingAssignments.length > 0 || isLoadingAssignments) && (
- <>
- <Separator />
- <div className="space-y-3">
- <Label className="text-sm font-medium flex items-center gap-2">
- <AlertCircle className="h-4 w-4" />
- 현재 할당 현황
- </Label>
-
- {isLoadingAssignments ? (
- <div className="flex items-center justify-center py-4">
- <Loader2 className="h-4 w-4 animate-spin mr-2" />
- 기존 할당 정보를 조회하는 중...
- </div>
- ) : (
- <div className="border rounded-md max-h-60 overflow-y-auto">
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead>부서</TableHead>
- <TableHead>현재 도메인</TableHead>
- <TableHead>할당일</TableHead>
- <TableHead>설명</TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {existingAssignments.map((assignment) => (
- <TableRow key={assignment.id}>
- <TableCell className="font-medium">
- {assignment.departmentName}
- </TableCell>
- <TableCell>
- <Badge
- variant={assignment.assignedDomain === 'evcp' ? 'default' : 'secondary'}
- >
- {getDomainLabel(assignment.assignedDomain)}
- </Badge>
- </TableCell>
- <TableCell className="text-sm text-muted-foreground">
- {new Date(assignment.createdAt).toLocaleDateString('ko-KR')}
- </TableCell>
- <TableCell className="max-w-xs truncate text-sm">
- {assignment.description || '-'}
- </TableCell>
- </TableRow>
- ))}
- </TableBody>
- </Table>
- </div>
- )}
-
- {hasConflicts && (
- <div className="bg-yellow-50 border-yellow-200 border rounded-md p-3">
- <div className="flex items-start gap-2">
- <AlertCircle className="h-4 w-4 text-yellow-600 mt-0.5" />
- <div className="text-sm">
- <div className="font-medium text-yellow-800">도메인 변경 주의</div>
- <div className="text-yellow-700">
- 일부 부서의 기존 도메인과 다른 도메인을 할당하려고 합니다.
- 기존 할당은 자동으로 비활성화됩니다.
- </div>
- </div>
- </div>
- </div>
- )}
- </div>
- </>
- )}
-
- <Separator />
-
- {/* 도메인 선택 */}
- <div className="space-y-2">
- <Label htmlFor="domain-select" className="text-sm font-medium">
- 할당할 도메인 *
- </Label>
- <Select value={selectedDomain} onValueChange={setSelectedDomain}>
- <SelectTrigger id="domain-select">
- <SelectValue placeholder="도메인을 선택하세요" />
- </SelectTrigger>
- <SelectContent>
- {DOMAIN_OPTIONS.map((option) => (
- <SelectItem key={option.value} value={option.value}>
- <div className="flex flex-col">
- <span className="font-medium">{option.label}</span>
- <span className="text-xs text-muted-foreground">
- {option.description}
- </span>
- </div>
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
-
- {selectedDomainInfo && (
- <div className="text-sm text-muted-foreground">
- <Badge variant="secondary" className="mr-2">
- {selectedDomainInfo.label}
- </Badge>
- {selectedDomainInfo.description}
- </div>
- )}
- </div>
-
- {/* 할당 사유/설명 */}
- <div className="space-y-2">
- <Label htmlFor="description" className="text-sm font-medium">
- 할당 사유 또는 설명 (선택사항)
- </Label>
- <Textarea
- id="description"
- placeholder="예: 구매 업무 담당자들에게 procurement 도메인 할당"
- value={description}
- onChange={(e) => setDescription(e.target.value)}
- rows={3}
- maxLength={500}
- />
- <div className="text-xs text-muted-foreground text-right">
- {description.length}/500
- </div>
- </div>
-
- {/* 주의사항 */}
- <div className="bg-muted/50 p-3 rounded-md">
- <div className="text-sm text-muted-foreground">
- <div className="font-medium mb-1">⚠️ 주의사항</div>
- <ul className="list-disc list-inside space-y-1 text-xs">
- <li>도메인 할당은 해당 부서 소속 사용자들의 메뉴 접근 권한에 영향을 줍니다.</li>
- <li>기존에 다른 도메인이 할당된 부서는 새로운 도메인으로 덮어씌워집니다.</li>
- <li>Knox 조직도 변경으로 인해 부서가 삭제된 경우, 해당 할당은 고립된 레코드가 됩니다.</li>
- </ul>
- </div>
- </div>
- </div>
- </div>
-
- <DialogFooter className="border-t pt-4">
- <Button
- variant="outline"
- onClick={() => onOpenChange(false)}
- disabled={isSubmitting || isLoading}
- >
- 취소
- </Button>
- <Button
- onClick={handleAssign}
- disabled={!canSubmit}
- >
- {isSubmitting || isLoading ? (
- <>
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
- 할당 중...
- </>
- ) : (
- `도메인 할당 (${selectedDepartments.length}개 부서)`
- )}
- </Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
- );
-} \ No newline at end of file